package rad.framework.security.encryption;
 
import javax.crypto.Cipher;
import javax.crypto.SecretKey;

import rad.framework.jce.KeyStoreUtil;

import org.apache.commons.lang.StringUtils;
import org.jboss.util.Base64;
 
/**
 * The JCE implementation of <code>Encryptor</code>.
 * 
 * @author Chunyun Zhao
 */
public class JceEncryptor implements Encryptor {
    public static final String DATA_ENCODING_SET = "UTF-8";
 
    // Required properties
    private String keyStoreLocation;
    private String keyStorePass;
    private String keyAlias;
    private String keyPass;
    
    // Optional properties
    private String cipherAlgorithm = "AES"; // Uses AES by default.
    public static final String encryptionPrefix = "{JCE}";
    
    private SecretKey secretKey;
 
    /* 
     * Encrypts the data. It doesn't encrypt the data if it is already encrypted.
     */
    public String encrypt(String clearData) {
        if (StringUtils.isEmpty(clearData)  || 
                clearData.startsWith(encryptionPrefix)) {
            return clearData;
        }
        byte[] encryptedBytes;
        try {
            Cipher cipher = Cipher.getInstance(cipherAlgorithm);
            cipher.init(Cipher.ENCRYPT_MODE, getSecretKey());
            encryptedBytes = cipher.doFinal(clearData.getBytes(DATA_ENCODING_SET));
        } catch (Exception e) {
            throw new RuntimeException("Unable to encrypt the data.", e);
        }
        return encryptionPrefix + Base64.encodeBytes(encryptedBytes);
    }
 
    /* 
     * Decrypts the data. It doesn't decrypt the data if it is not encrypted.
     */    
    public String decrypt(String encryptedData) {
        if (StringUtils.isEmpty(encryptedData) || 
                !encryptedData.startsWith(encryptionPrefix) ) {
            return encryptedData;
        }
 
        encryptedData = encryptedData.substring(encryptionPrefix.length());
 
        byte[] decryptedBytes;
        try {
            Cipher cipher = Cipher.getInstance(cipherAlgorithm);

            cipher.init(Cipher.DECRYPT_MODE, getSecretKey());
            byte[] decodedBytes = Base64.decode(encryptedData);
            decryptedBytes = cipher.doFinal(decodedBytes);
            return new String(decryptedBytes, DATA_ENCODING_SET);
        } catch (Exception e) {
            throw new RuntimeException("Unable to descrypt the data.", e);
        }
    }
    
    public SecretKey getSecretKey() {
        if ( secretKey == null ) {
        	secretKey = (SecretKey) KeyStoreUtil.getSecretKey(keyStoreLocation, keyStorePass, keyAlias, keyPass);

        	if (secretKey == null) {
        		throw new RuntimeException("Unable to decrypt the data. Secret key : " 
        				+ keyAlias +
        		" couldn't be retrieved from keystore manager.");
        	}
        }
        return secretKey;
    }

	public String getCipherAlgorithm() {
		return cipherAlgorithm;
	}

	public void setCipherAlgorithm(String cipherAlgorithm) {
		this.cipherAlgorithm = cipherAlgorithm;
	}

	public String getKeyAlias() {
		return keyAlias;
	}

	public void setKeyAlias(String keyAlias) {
		this.keyAlias = keyAlias;
	}

	public String getKeyPass() {
		return keyPass;
	}

	public void setKeyPass(String keyPass) {
		this.keyPass = keyPass;
	}

	public String getKeyStoreLocation() {
		return keyStoreLocation;
	}

	public void setKeyStoreLocation(String keyStoreLocation) {
		this.keyStoreLocation = keyStoreLocation;
	}

	public String getKeyStorePass() {
		return keyStorePass;
	}

	public void setKeyStorePass(String keyStorePass) {
		this.keyStorePass = keyStorePass;
	}
} 